Use case: Clock calibration (polling)

The configuration and status monitoring of the FDCAN clock calibration have been significantly improved from HAL1 to HAL2:

  • Clock calibration configuration

    • In HAL1, the FDCAN clock calibration is configured using the single function HAL_FDCAN_ConfigClockCalibration(), which accepts a configuration structure containing parameters such as time quanta per bit time, calibration field length, minimum oscillator clock periods, watchdog start value, clock calibration enable flag, and clock divider. When the clock calibration is disabled by setting ClockCalibration = FDCAN_CLOCK_CALIBRATION_DISABLE, the clock divider specified in the configuration is used to adjust the FDCAN clock prescaler.

    • In HAL2, the clock calibration configuration is modularized for greater flexibility and control. The function HAL_FDCAN_CCU_SetClockRouting() selects the clock routing path, allowing the user to enable the clock calibration unit (e.g., HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION) or bypass it (HAL_FDCAN_CCU_CLOCK_ROUTE_BYPASS). Calibration parameters are then set separately using HAL_FDCAN_CCU_SetConfigCalibrationUnit(). When the clock calibration unit is bypassed, the FDCAN clock calibration is disabled, and the clock divider can be explicitly configured using HAL_FDCAN_SetClockDivider() to set the clock prescaler.

  • Get clock calibration state

    • In HAL1, checking the FDCAN clock calibration status requires multiple steps: waiting for the calibration state change flag using __HAL_FDCAN_GET_FLAG(), then retrieving the calibration state with HAL_FDCAN_GetClockCalibrationState(), followed by manually clearing the calibration state change flag using __HAL_FDCAN_CLEAR_FLAG().

    • In HAL2, this process is streamlined into a single function, HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(), which reads the current calibration level and clears the calibration state change flag internally.

In both HAL1 and HAL2, when calibration is complete or the user wants to exit restricted mode, the user must call the appropriate function: in HAL1, HAL_FDCAN_ExitRestrictedOperationMode(), and in HAL2, HAL_FDCAN_DisableRestrictedOperationMode().

Sequence diagram

  @startuml
  participant App as "User Application"
  participant "<font color=black><b>HAL FDCAN" as FDCAN #19DD3A

  participant "FDCAN Hardware System" as FDCAN_HW

  == FDCAN Initialization and Configuration ==
  App -> FDCAN: HAL_FDCAN_Init(&hfdcan)
  FDCAN --> App: <color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig)
  FDCAN --> App: <color green>HAL_OK

  == Configure Reception Filters ==
  App -> FDCAN: HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig) [Calibration Filter]
  note right
     Configure at least one filter
     with IsCalibrationMsg = 1
  endnote
  FDCAN --> App: <color green>HAL_OK
  App-> FDCAN: HAL_FDCAN_Start()
  App <-- FDCAN:<color green>HAL_OK

  App -> FDCAN: HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER0)
  FDCAN_HW <- : FDCAN **First** Rx buffer new message
  note right
   First calibration Message available
   endnote
  FDCAN --> App: 1 (available)

  loop  Wait for calibration state change flag
    App -> FDCAN: __HAL_FDCAN_GET_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
    FDCAN --> App: 1 (flag set)
  end
  App -> FDCAN: HAL_FDCAN_GetClockCalibrationState(&hfdcan)
  FDCAN_HW --> FDCAN_HW: Basic calibration done and state changed
  hnote over FDCAN_HW
  <font color=orange><b>BASIC_CALIBRATED</b>
  end note
  FDCAN --> App: <font color=orange>BASIC_CALIBRATED

  App -> FDCAN: __HAL_FDCAN_CLEAR_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
  FDCAN --> App: <color green>HAL_OK
  FDCAN_HW <- : FDCAN **second** Rx buffer new message
  note right
   Second calibration Message available
   endnote
  FDCAN --> App: 1 (available)
  loop Wait for calibration state change flag
    App -> FDCAN: __HAL_FDCAN_GET_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
    FDCAN --> App: 1 (flag set)
  end

  App -> FDCAN: HAL_FDCAN_GetClockCalibrationState(&hfdcan)
  FDCAN_HW --> FDCAN_HW: Precision calibration done and state changed
  hnote over FDCAN_HW
  <font color=green><b>PRECISION_CALIBRATED</b>
  end note
  FDCAN --> App: <font color=green> PRECISION_CALIBRATED
  == Exit Restricted Operation Mode ==
  App -> FDCAN: HAL_FDCAN_ExitRestrictedOperationMode(&hfdcan)
  hNote over FDCAN_HW: The FDCAN in <color green><b>Normal Operation Mode</b></color>
  FDCAN --> App: <color green>HAL_OK
  Note over App: The FDCAN is calibrated and ready to be used for communication.

  @enduml

Code snippet

HAL1

HAL2

hfdcan.Instance                = FDCAN1;
hfdcan.Init.FrameFormat        = FDCAN_FRAME_CLASSIC;
hfdcan.Init.Mode               = FDCAN_MODE_NORMAL;
hfdcan.Init.AutoRetransmission = DISABLE;
hfdcan.Init.TransmitPause      = DISABLE;
hfdcan.Init.ProtocolException  = ENABLE;


hfdcan.Init.NominalPrescaler     = 0x1;
hfdcan.Init.NominalTimeSeg1      = 0xF;
hfdcan.Init.NominalTimeSeg2      = 0x4;
hfdcan.Init.NominalSyncJumpWidth = 0x4;

hfdcan.Init.MessageRAMOffset    = 0;
hfdcan.Init.StdFiltersNbr       = 2;
hfdcan.Init.ExtFiltersNbr       = 0;
hfdcan.Init.RxFifo0ElmtsNbr     = 0;
hfdcan.Init.RxFifo1ElmtsNbr     = 0;
hfdcan.Init.RxBuffersNbr        = 2;
hfdcan.Init.RxBufferSize        = FDCAN_DATA_BYTES_8;
hfdcan.Init.TxEventsNbr         = 0;
hfdcan.Init.TxBuffersNbr        = 1;
hfdcan.Init.TxFifoQueueElmtsNbr = 0;
hfdcan.Init.TxElmtSize          = FDCAN_DATA_BYTES_8;
HAL_FDCAN_Init(&hfdcan);

sCcuConfig.ClockCalibration     = ENABLE;


sCcuConfig.MinOscClkPeriods     = 4;
sCcuConfig.CalFieldLength       = FDCAN_CALIB_FIELD_LENGTH_64;
sCcuConfig.TimeQuantaPerBitTime = 20;

HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig);

sFilterConfig.IdType           = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex      = 0;
sFilterConfig.FilterType       = FDCAN_FILTER_DUAL;
sFilterConfig.FilterConfig     = FDCAN_FILTER_TO_RXBUFFER;
sFilterConfig.FilterID1        = 0x555;
sFilterConfig.RxBufferIndex    = 0;
sFilterConfig.IsCalibrationMsg = 1;

HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig);

HAL_FDCAN_Start(&hfdcan);

while(HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER0) == 0);

while(__HAL_FDCAN_GET_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED) == 0);
if(HAL_FDCAN_GetClockCalibrationState(&hfdcan) != FDCAN_CLOCK_BASIC_CALIBRATED)
{
  Error_Handler();
}
__HAL_FDCAN_CLEAR_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED);

while(__HAL_FDCAN_GET_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED) == 0);
if(HAL_FDCAN_GetClockCalibrationState(&hfdcan) != FDCAN_CLOCK_PRECISION_CALIBRATED)
{
  Error_Handler();
}

HAL_FDCAN_ExitRestrictedOperationMode(&hfdcan);
HAL_FDCAN_Init(&hfdcan, HAL_FDCAN1);
fdcan_config.frame_format        = HAL_FDCAN_FRAME_FORMAT_CLASSIC_CAN;
fdcan_config.mode                = HAL_FDCAN_MODE_NORMAL;
fdcan_config.auto_retransmission = HAL_FDCAN_AUTO_RETRANSMISSION_ENABLE;
fdcan_config.transmit_pause      = HAL_FDCAN_TRANSMIT_PAUSE_DISABLE;
fdcan_config.protocol_exception  = HAL_FDCAN_PROTOCOL_EXCEPTION_ENABLE;

hal_fdcan_nominal_bit_timing_t fdcan_nominal_bit_timing;
fdcan_nominal_bit_timing.nominal_prescaler  = 0x1;
fdcan_nominal_bit_timing.nominal_time_seg1  = 0xF;
fdcan_nominal_bit_timing.nominal_time_seg2  = 0x4;
fdcan_nominal_bit_timing.nominal_jump_width = 0x4;
fdcan_config.nominal_bit_timing             = fdcan_nominal_bit_timing;
fdcan_config.message_ram_offset             = 0;
fdcan_config.std_filters_nbr                = 2;
fdcan_config.ext_filters_nbr                = 0;
fdcan_config.rx_fifo0_elt_nbr               = 0;
fdcan_config.rx_fifo1_elt_nbr               = 0;
fdcan_config.rx_buffer_nbr                  = 2;
fdcan_config.rx_buffer_size                 = HAL_FDCAN_DATA_BYTES_8;
fdcan_config.tx_event_nbr                   = 0;
fdcan_config.tx_buffer_nbr                  = 1;
fdcan_config.tx_fifo_queue_elt_nbr          = 0;
fdcan_config.tx_fifo_queue_elt_size         = HAL_FDCAN_DATA_BYTES_8;
HAL_FDCAN_SetConfig(&hfdcan, &fdcan_config);

hal_fdcan_ccu_clock_route_t clock_route = HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION;
HAL_FDCAN_CCU_SetClockRouting(&hfdcan, clock_route);

ccu_config.min_osc_clk_periods          = 4;
ccu_config.calibration_field_length     = HAL_FDCAN_CCU_FIELD_LENGTH_64;
ccu_config.time_quanta_per_bit_time     = 20;

HAL_FDCAN_CCU_SetConfigCalibrationUnit(&hfdcan, &ccu_config);

filter_calibration.id_type         = HAL_FDCAN_ID_STANDARD,
filter_calibration.filter_index    = 0U,
filter_calibration.filter_type     = HAL_FDCAN_FILTER_TYPE_DUAL,
filter_calibration.filter_config   = HAL_FDCAN_FILTER_TO_RX_BUFFER,
filter_calibration.filter_id1      = 0x555,
filter_calibration.rx_buffer_index = HAL_FDCAN_RX_BUFFER0,
filter_calibration.message_type    = HAL_FDCAN_MESSAGE_CALIBRATION,

 HAL_FDCAN_SetFilter(&hfdcan, &filter_calibration);

HAL_FDCAN_Start(&hfdcan);

while(HAL_FDCAN_GetRxBufferMessageStatusAndClearFlag(&hfdcan, HAL_FDCAN_RX_BUFFER0) == 0);

if(HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(&hfdcan, 10) != HAL_FDCAN_CCU_CAL_LEVEL_BASIC_CALIBRATED)
{
  Error_Handler();
}



if(HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(&hfdcan, 10) != HAL_FDCAN_CCU_CAL_LEVEL_PRECISION_CALIBRATED)
{
  Error_Handler();
}


HAL_FDCAN_DisableRestrictedOperationMode(&hfdcan);

Key changes

  1. HAL2 modularizes clock calibration configuration with dedicated CCU routing and configuration APIs.

  2. HAL2 streamlines calibration status monitoring with a single API that also clears the status flag.